# 第七章 后端商品分类管理

正所谓人以群分,物以类聚,在一个电商应用中,我们会有成千上万的商品,而这些商品又可以按照其特征或某些共有的属性来对其归类,通过对商品的归类,可以实现前端APP用户或者后端CMS管理员快速检索某个类型的一系列商品,比如说我们常用的饿了么外卖APP,每个商家都会有个菜谱,菜谱里面会有热菜、凉菜、熟食等分类,你点击某个分类就会向你展示该分类下的菜品让你可以快速找到想要吃的菜品。我们原型APP里的每个商品同样都有属于自己的分类,本章节将带着大家学习如何对商品分类进行管理。

# 查询商品分类

首先我们来实现查询所有商品分类的功能,在控制器层下新建Category控制器类,在控制器类中新增一个getCategory()控制器方法:

<?php


namespace app\api\controller\v1;

use app\api\model\Category as CategoryModel;

class Category
{
    /**
     * 查询所有商品分类
     */
    public function getCategory()
    {
        
    }
}    
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

在原型APP的项目中,我们的商品分类存放于category表,所以这里我们必须在模型层下创建一个Category模型类:

<?php


namespace app\api\model;


class Category extends BaseModel
{
    protected $hidden = ['delete_time', 'create_time', 'update_time','topic_img_id'];

    public function img()
    {
        return $this->belongsTo('Image', 'topic_img_id');

    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

由于category表有个关联了image表的字段,这里我们同样定义了一个方法来声明关联关系用于获取对应图片信息。定义好模型类之后,让我们回到控制器方法中调用一下:

<?php


namespace app\api\controller\v1;

use app\api\model\Category as CategoryModel;

class Category
{
    /**
     * 查询所有商品分类
     */
    public function getCategory()
    {
        $result = CategoryModel::with('img')->select();
        if ($result->isEmpty()) {
            throw new CategoryException([
                'code'=>404,
                'msg' => '没有查询到商品分类', 
                'error_code' => 70003]);
        }

        return $result;
    }
}    
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

这里同样是熟悉的查询操作,调用了模型关联查询,当查询为空时抛出一个异常信息。控制器和模型定义完之后就轮到定义路由了,打开route.php文件,我们新增一个category路由分组,并在分组下定义一条路由规则:

Route::group('', function () {
    Route::group('cms', function () {
        // CMS管理相关的路由规则
        // 内容省略。。。。
    });
    Route::group('v1', function () {
        // 业务接口相关的路由规则
        ............................
        ............................
        // 分类相关接口
        Route::group('category', function () {
            // 查询所有分类
            Route::get('', 'api/v1.Category/getCategory');
        });
    });
})->middleware(['Auth','ReflexValidate'])->allowCrossDomain();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

这里我们定义了一条GET请求,打开Postman,同样创建一个分组,并在分组下新增一个请求,把我们的路由信息填写进去:

点击发送,得到结果:

[
    {
        "id": 2,
        "name": "水果",
        "description": "收到",
        "img": {
            "id": 130,
            "url": "http://localhost:8000/images/20190817/04e87b726c49f9c857859fe7a970f97f.png"
        }
    },
    {
        "id": 3,
        "name": "蔬菜",
        "description": "撒大声地",
        "img": {
            "id": 131,
            "url": "http://localhost:8000/images/20190817/04e87b726c49f9c857859fe7a970f97f.png"
        }
    },
    {
        "id": 4,
        "name": "炒货",
        "description": "阿斯顿撒",
        "img": {
            "id": 7,
            "url": "http://localhost:8000/images/category-fry-a.png"
        }
    },
    {
        "id": 5,
        "name": "点心",
        "description": "奥术大师",
        "img": {
            "id": 4,
            "url": "http://localhost:8000/images/category-cake.png"
        }
    },
    {
        "id": 6,
        "name": "粗茶",
        "description": "阿萨德",
        "img": {
            "id": 8,
            "url": "http://localhost:8000/images/category-tea.png"
        }
    },
    {
        "id": 7,
        "name": "淡饭",
        "description": null,
        "img": {
            "id": 9,
            "url": "http://localhost:8000/images/category-rice.png"
        }
    }
]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

没有报错,这里我们顺利拿到了数据中已经存在的商品分类信息,这个接口后面将会为前端APP或者CMS提供显示分类列表的数据支持。

# 新增商品分类

随着业务的发展,我们的商品越来越多,现有的商品分类不足以分类现有的商品了,这时候自然就需要增加分类了,实现方法很简单,在Category控制器类下新增一个addCategory()方法:

<?php


namespace app\api\controller\v1;

use app\api\model\Category as CategoryModel;
use app\lib\exception\category\CategoryException;
use think\facade\Request;

class Category
{
    /** 查询所有商品分类*/
    public function getCategory(){...}

    /**
     * 新增商品分类
     * @validate('CategoryForm')
     */
    public function addCategory()
    {
        $params = Request::post();
        $category = CategoryModel::create($params, true);
        if (!$category) {
            throw new CategoryException(['msg' => '商品分类创建失败', 'error_code' => 70004]);
        }

        return writeJson(201, [], '商品分类创建成功!');
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

一个套路满满的控制器方法,获取参数,调用模型方法,注解验证器,异常输出,在熟练地运用lin-cms-tp5的特性之后,接口开发就是如此简单。这里我们要创建一下这个自定义验证器,打开我们用于存放自定义验证器的目录,新建一个CategoryForm.php文件,并定义如下:

<?php


namespace app\api\validate\category;


use LinCmsTp5\validate\BaseValidate;

class CategoryForm extends BaseValidate
{
    protected $rule = [
        'name' => 'require|chsDash',
        'description' => 'require|chsDash',
        'topic_img_id' => 'require|number',
    ];
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

这里我们要求创建商品分类的时候必须传递这三个参数,并利用tp5的内置校验规则对参数的类型做了校验。当创建分类失败的时候我还需要抛出一个异常,所以我们还需要定义一个自定义异常类,同样在我们用于存放自定义异常类的目录下新建一个CategoryException.php文件,定义如下:

<?php


namespace app\lib\exception\category;


use LinCmsTp5\exception\BaseException;

class CategoryException extends BaseException
{
    public $code = 400;
    public $msg  = '商品分类接口异常';
    public $error_code = '70000';
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

当商品分类创建失败的时候,我们抛出了这个异常并对这三个成员属性重新赋值。最后就是定义路由规则了,打开route.php文件,在category分组下新增一条规则:

Route::group('', function () {
    Route::group('cms', function () {
        // CMS管理相关的路由规则
        // 内容省略。。。。
    });
    Route::group('v1', function () {
        // 业务接口相关的路由规则
        ............................
        ............................
        // 分类相关接口
        Route::group('category', function () {
            // 查询所有分类
            Route::get('', 'api/v1.Category/getCategory');
            // 新增分类
            Route::post('', 'api/v1.Category/addCategory');
        });
    });
})->middleware(['Auth','ReflexValidate'])->allowCrossDomain();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

这里我们给新增商品分类这个操作定义了一个POST请求,定义好之后,让我们打开Postman,新建一个请求并按路由规则配置:

这里我们来试着创建一个新的商品分类,按接口的验证规则填写几个测试参数,发送请求:

{
    "error_code": 0,
    "result": [],
    "msg": "商品分类创建成功!"
}
1
2
3
4
5

提示我们创建成功了,这时候我们调一下上节实现的查询分类接口来验证一下:

[
    .............
    .............

    {
        "id": 8,
        "name": "哇塞多",
        "description": "www",
        "img": {
            "id": 14,
            "url": "http://localhost:8000/images/product-rice@6.png"
        }
    }
]
1
2
3
4
5
6
7
8
9
10
11
12
13
14

可以看到这里的分类信息就是我们刚刚提交的测试参数,说明我们的商品分类已经成功创建了。

# 编辑商品分类

在上一小节中,我们学习了如何新增一个商品分类,在最后的测试环节,我们新增了一个很随意的商品分类,这样的商品分类显然是没有实际意义的,所以我们要实现下编辑商品分类的接口才行,说来就来,在控制器层的Category类下新增一个updateCategory()方法:

<?php


namespace app\api\controller\v1;

use app\api\model\Category as CategoryModel;
use app\lib\exception\category\CategoryException;
use think\facade\Request;

class Category
{
    /** 查询所有商品分类*/
    public function getCategory(){...}
    /** 新增商品分类*/
    public function addCategory(){...}
    /**
     * 更新指定分类信息
     * @validate('CategoryForm.edit')
     */
    public function updateCategory($id)
    {
        $params = Request::put();
        $category = CategoryModel::get($id);
        if (!$category) {
            throw new CategoryException([
                'code' => 404,
                'msg' => '指定id的分类不存在',
                'error_code' => 70003]);
        }
        // allowField(true)只允许写入数据表存在的字段
        $category->allowField(true)->save($params);

        return writeJson(201, [], '商品分类更新成功!');
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

这里同样是熟悉的套路,这个控制器方法接收一个$id参数,是要修改的商品分类id,在查询出对应商品分类记录之后,调用save()方法并把请求的参数传递进去实现字段内容更新。注意这里我们使用了验证场景的功能,所以我们要在上一小节创建的自定义验证器中声明下这个验证场景,打开我们的CategoryForm自定义验证器类:

<?php


namespace app\api\validate\category;


use LinCmsTp5\validate\BaseValidate;

class CategoryForm extends BaseValidate
{
    protected $rule = [
        'name' => 'require|chsDash',
        'description' => 'require|chsDash',
        'topic_img_id' => 'require|number',
    ];

   /**
    * 声明一个名叫edit的场景
    */
    public function sceneEdit()
    {
        return $this->append('id', 'require|number')
            ->remove('description', 'require')
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

这里我们声明了一个edit的验证场景,当触发这个场景时,我们追加了一条验证规则,即必须传递id这个参数并移除description参数不能为空的限制。到这里接口就实现完毕了,打开route.php,在category分组下新增一条路由规则:

Route::group('', function () {
    Route::group('cms', function () {
        // CMS管理相关的路由规则
        // 内容省略。。。。
    });
    Route::group('v1', function () {
        // 业务接口相关的路由规则
        ............................
        ............................
        // 分类相关接口
        Route::group('category', function () {
            // 查询所有分类
            Route::get('', 'api/v1.Category/getCategory');
            // 新增分类
            Route::post('', 'api/v1.Category/addCategory');
            // 编辑商品分类
            Route::put(':id', 'api/v1.Category/updateCategory');
        });
    });
})->middleware(['Auth','ReflexValidate'])->allowCrossDomain();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

这里我们定义了一个PUT请求,对应到们刚刚实现的updateCategory()方法,路由定义完成之后就可以到Postman中来测试下了,老习惯,新增一个请求,按定义的路由规则配置:

这里作者尝试修改下上一小节创建了的商品分类,在请求地址的最后附上这个商品记录的id,在Body中传递一段JSON参数,这里我把分类的名称改为——酒水,简介留空,然后随便指定了一个图片id,点击发送:

{
    "error_code": 0,
    "result": [],
    "msg": "商品分类更新成功!"
}
1
2
3
4
5

没有报错,让我们再次调用查询所有分类接口来验证一下:

[
    .................
    .................
    {
        "id": 8,
        "name": "酒水",
        "description": "",
        "img": {
            "id": 1,
            "url": "http://localhost:8000/images/banner-1a.png"
        }
    }
]
1
2
3
4
5
6
7
8
9
10
11
12
13

从结果可以看出我们的编辑内容已经生效了,这说明我们的编辑商品分类接口已经可以正常工作了。

# 删除商品分类

在上一小节中,我们实现了对商品分类的编辑,不过有时候根据业务情况的调整,有些分类我们将暂时或不再使用,这时候就需要删除这一个分类,实现的方式同样简单,在控制层的Category控制器类下新增一个delCategory()方法:

<?php


namespace app\api\controller\v1;

use app\api\model\Category as CategoryModel;
use app\lib\exception\category\CategoryException;
use think\facade\Request;

class Category
{
    /** 查询所有商品分类*/
    public function getCategory(){...}
    /** 新增商品分类*/
    public function addCategory(){...}
    /** 更新指定分类信息*/
    public function updateCategory($id){...}
    /**
     * 删除商品分类
     * @auth('删除商品分类','商品管理')
     * @param('ids','待删除的商品分类id列表','require|array|min:1')
     */
    public function delCategory()
    {
        $ids = Request::delete('ids');
        // 执行软删除
        CategoryModel::destroy($ids);
        Hook::listen('logger', '删除了id为' . implode(',', $ids) . '的商品分类');

        return writeJson(201, [], '商品分类删除成功!');
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

这里我们同样给删除商品分类接口添加了接口的权限控制和行为日志功能,通过给接口传递$ids参数实现商品分类的删除,注意这里我们同样采用软删除的形式,所以需要在Category模型中加入use SoftDelete的引用:

<?php


namespace app\api\model;


use think\model\concern\SoftDelete;

class Category extends BaseModel
{
    use SoftDelete;

    protected $hidden = ['delete_time', 'create_time', 'update_time','topic_img_id'];

    public function img()
    {
        return $this->belongsTo('Image', 'topic_img_id');

    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

控制器方法和模型定义完之后,自然就是定义路由了,打开route.php,新增一条路由规则:

Route::group('', function () {
    Route::group('cms', function () {
        // CMS管理相关的路由规则
        // 内容省略。。。。
    });
    Route::group('v1', function () {
        // 业务接口相关的路由规则
        ............................
        ............................
        // 分类相关接口
        Route::group('category', function () {
            // 查询所有分类
            Route::get('', 'api/v1.Category/getCategory');
            // 新增分类
            Route::post('', 'api/v1.Category/addCategory');
            // 编辑商品分类
            Route::put(':id', 'api/v1.Category/updateCategory');
            // 删除分类
            Route::delete('', 'api/v1.Category/delCategory');
        });
    });
})->middleware(['Auth','ReflexValidate'])->allowCrossDomain();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

这里我们给删除商品分类接口定义了一个DELETE请求,路由定义完之后,打开Postman,新增一个请求并按照路由规则配置一下:

这里我们尝试删除商品分类id为7、8的两个商品分类,这里要注意一点就是,由于我们给这个接口添加了权限控制和行为日志,所以要请求这个接口必须携带令牌,利用前面教给大家获取令牌的方法,我们把令牌信息配置到Postman中:

配置完成了之后我们就可以试着调用一下了,点击发送:

{
    "error_code": 0,
    "result": [],
    "msg": "商品分类删除成功!"
}
1
2
3
4
5

没有报错,我们调用一下查询商品分类接口来验证下:

[
    {
        "id": 2,
        "name": "水果",
        "description": "",
        "img": {
            "id": 130,
            "url": "http://localhost:8000/images/20190817/04e87b726c49f9c857859fe7a970f97f.png"
        }
    },
    {
        "id": 3,
        "name": "蔬菜",
        "description": "撒大声地",
        "img": {
            "id": 131,
            "url": "http://localhost:8000/images/20190817/04e87b726c49f9c857859fe7a970f97f.png"
        }
    },
    {
        "id": 4,
        "name": "炒货",
        "description": "阿斯顿撒",
        "img": {
            "id": 7,
            "url": "http://localhost:8000/images/category-fry-a.png"
        }
    },
    {
        "id": 5,
        "name": "点心",
        "description": "奥术大师",
        "img": {
            "id": 4,
            "url": "http://localhost:8000/images/category-cake.png"
        }
    },
    {
        "id": 6,
        "name": "粗茶",
        "description": "阿萨德",
        "img": {
            "id": 8,
            "url": "http://localhost:8000/images/category-tea.png"
        }
    }
]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

可以看到我们原来id为7和8的商品分类已经查询不到了,这说明我们的删除商品分类接口已经正常工作了。

最后更新: 2021-08-12 13:31:59
0/140
评论
0
暂无评论
  • 上一页
  • 首页
  • 1
  • 尾页
  • 下一页
  • 总共1页